#pragma once

#include "Runnable.h"
#include "RunnableThread.h"
#include "IThreadWork.h"
#include "ThreadPool.h"
#include <atomic>
#include <future>

/**
 * This is the interface used for all poolable threads. The usage pattern for
 * a poolable thread is different from a regular thread and this interface
 * reflects that. Queued threads spend most of their life cycle idle, waiting
 * for work to do. When signaled they perform a job and then return themselves
 * to their owning pool via a callback and go back to an idle state.
 */
class WorkThread : public Runnable
{
protected:

	std::atomic_bool mTimeToDie;

	IThreadWork* volatile mThreadWork;

	RunnableThread* mThread;

	/** The event that tells the thread there is work to do. */
	HANDLE mDoWorkEvent;

	std::shared_future<void> mStartFuture;

public:

	WorkThread(std::shared_future<void>& startFuture);

	uint32 Run() override;

	/**
	 * Creates the thread with the specified stack size and creates the various
	 * events to be able to communicate with it.
	 *
	 * @param InPool The thread pool interface used to place this thread back into the pool of available threads when its work is done
	 * @param InStackSize The size of the stack to create. 0 means use the current thread's stack size
	 * @param ThreadPriority priority of new thread
	 * @return True if the thread and all of its initialization was successful, false otherwise
	 */
	bool Create(ThreadPriority threadPriority = ThreadPriority::TPri_Normal);

	/**
	 * Tells the thread to exit. If the caller needs to know when the thread
	 * has exited, it should use the bShouldWait value and tell it how long
	 * to wait before deciding that it is deadlocked and needs to be destroyed.
	 * NOTE: having a thread forcibly destroyed can cause leaks in TLS, etc.
	 *
	 * @return True if the thread exited graceful, false otherwise
	 */
	bool KillThread();

	/**
	 * Tells the thread there is work to be done. Upon completion, the thread
	 * is responsible for adding itself back into the available pool.
	 *
	 * @param InQueuedWork The queued work to perform
	 */
	void DoWork(IThreadWork* threadWork);

};